package com.maixy.redisdemo.controller;

import com.maixy.redisdemo.service.RedisUserService;
import com.maixy.redisdemo.utils.RedisLockUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName RedisUserController
 * @Description TODO
 * @Date 2021/5/16 19:16
 * @Author maixy
 */
@RestController
@RequestMapping("/redis/user")
@Slf4j
public class RedisUserController {
    @Resource
    private RedisUserService redisUserService;

    @Autowired
    private RedisLockUtil redisLockUtil;

    /**
     * 锁测试共享变量
     */
    private Integer lockCount = 10;

    /**
     * 无锁测试共享变量
     */
    private Integer count = 10;

    /**
     * 模拟线程数
     */
    private static int threadNum = 10;

    /**
     * 模拟并发测试加锁和不加锁
     */
    @GetMapping("/lock")
    public void lock(){
        // 计数器
        final CountDownLatch countDownLatch = new CountDownLatch(1);
        for (int i = 0; i < threadNum; i ++) {
            MyRunnable myRunnable = new MyRunnable(countDownLatch);
            Thread myThread = new Thread(myRunnable);
            myThread.start();
        }
        // 释放所有线程
        countDownLatch.countDown();
    }

    /**
     * 加锁测试
     */
    private void testLockCount() {
        String lockKey = "lock-test";
        try {
            // 加锁，设置超时时间2s
            redisLockUtil.lock(lockKey, TimeUnit.SECONDS,2);
            lockCount--;
            log.info("lockCount值："+lockCount);
        }catch (Exception e){
            log.error(e.getMessage(),e);
        }finally {
            // 释放锁
            redisLockUtil.unlock(lockKey);
        }
    }

    /**
     * 无锁测试
     */
    private void testCount() {
        count--;
        log.info("count值："+count);
    }


    public class MyRunnable implements Runnable {
        /**
         * 计数器
         */
        final CountDownLatch countDownLatch;
        public MyRunnable(CountDownLatch countDownLatch) {
            this.countDownLatch = countDownLatch;
        }

        @Override
        public void run() {
            try {
                // 阻塞当前线程，直到计时器的值为0
                countDownLatch.await();
            } catch (InterruptedException e) {
                log.error(e.getMessage(),e);
            }
            // 无锁操作
            testCount();
            // 加锁操作
            testLockCount();
        }
    }

    @GetMapping("/insert")
    public Object insert() {
        String lockKey = "lock-insert";
        //等待时间很关键，我这个接口的目的是限制频繁操作，所以等待时间为0秒
        //如果是为了防止超卖可以设置一定的等待时间（这个我还没有实践，只是猜测）
        if (redisLockUtil.tryLock(lockKey,TimeUnit.SECONDS,0, 10)) {
            try {

                log.info("正常执行");
                Thread.sleep(1000);
            }catch (Exception e){
                log.error(e.getMessage(),e);
            }finally {
                // 释放锁
                redisLockUtil.unlock(lockKey);
            }
        } else {
            log.error("请勿重复操作！！！");
            return "请勿重复操作！！！";
        }
        return "ok";
    }

    @GetMapping("/find/{id}")
    public Object find(@PathVariable("id") Long id) {
        return redisUserService.find(id);
    }

    @GetMapping("/insert1")
    public Object insert1() {
        return redisUserService.insert1();
    }

}
